import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;

/**
 * Created by obila_000 on 2014/10/20.
 */
/**
 * Overview: an AnimationWindow is an area on the screen in which a
 * bouncing ball animation occurs.  AnimationWindows have two modes:
 * on and off.  During the on mode the ball moves, during the off
 *  mode the ball doesn't move.
 *  游戏的动画窗口，负责绘制动画，有两个模式，运行模式运行游戏，编辑模式绘制网格添加移动图形
 */

public  class AnimationWindow extends JComponent {
    enum Anim_mode{
        RUNNING_MODE,
        STOP_MODE,
        EDIT_MODE
    }

    //定义ID修饰词
    final static String SQURE_LETTER = "S";
    final static String CIRCLE_LETTER = "C";
    final static String TRANGLE_LETTER = "T";
    final static String FLIPPER_LEFT_LETTER = "LF";
    final static String FLIPPER_RIGHT_LETTER = "RF";
    final static String LBLOCK_LETTER = "LB";



    final static int ROWS = 20;      //网格行数
    final static int COLUMNS = 20;  //网格列数
    final static float LSIZE = 35.0f; //网格默认长度
    final static float FLIPPER_HEIGHT = 10.0f;  //flipper默认厚度

    private static final long serialVersionUID = 3257281448464364082L;
    private static int FRAMES_PER_SECOND = 100;  //帧数每秒

    private AnimationEventListener eventListener;
    private BouncingBall ball;
    private Timer timer;
    private Anim_mode mode;

    private int currentAreaRow;         //鼠标在网格的行数
    private int currentAreaColumn;      //鼠标在网格的列数
    Point mousePosition;                //鼠标位置

    ArrayList<Gizmos> allGizmos;        //所有的小物件
    Gizmos currentChoosedGizmos;        //当前选中的小物件
    Gizmos.Gizmos_type currentType;     //选中小物件的类型
    private boolean isNewItem;                  //是否添加的是新物体
    private boolean isItemChosed;               //当前是否有物体选中
    private boolean isAddConnectState;          //是否是需要绑定状态

//    public Flipper testLeftFlipper;     //测试用的左弹板
//    public Flipper testRightFlipper;



    /**
     * @effects initializes this to be in the off mode.
     */
    public AnimationWindow() {

        super(); // do the standard JPanel setup stuff

        ball = new BouncingBall(this);

        // this only initializes the timer, we actually start and stop the
        // timer in the setMode() method
        eventListener = new AnimationEventListener();

        // The first parameter is how often (in milliseconds) the timer
        // should call us back.
        timer = new Timer(1000 / FRAMES_PER_SECOND, eventListener);
        allGizmos = new ArrayList<Gizmos>();
        mode = Anim_mode.STOP_MODE;
        currentAreaRow = 0;
        currentAreaColumn = 0;
        mousePosition = new Point(0,0);
        currentChoosedGizmos = null;
        currentType = null;
        isNewItem = false;
        isAddConnectState = false;
    }

    /**
     * 载入新的游戏地图
     * @param newGizmos
     * @param newBall
     */
    public void refreashWindow(ArrayList<Gizmos> newGizmos, BouncingBall newBall){
        removeMouseListener(eventListener);
        removeMouseMotionListener(eventListener);
        removeKeyListener(eventListener);
        allGizmos.clear();
        allGizmos = newGizmos;
        ball = newBall;
        addMouseListener(eventListener);
        addMouseMotionListener(eventListener);
        addKeyListener(eventListener);
        repaint(0,0,1000,1000);
    }


    /**
     * @modifies g
     * @effects Repaints the Graphics area g.  Swing will then send the newly painted g to the screen.
     * @param g Graphics context received by either system or app calling repaint()
     */
    @Override public void paintComponent(Graphics g) {
        // first repaint the proper background color (controlled by
        // the windowing system)
        //super.paintComponent(g);
        Graphics2D graphics2D = (Graphics2D)g;
        if(mode == Anim_mode.EDIT_MODE) {
            for (int i = 0; i < ROWS; i++) {
                for (int j = 0; j < COLUMNS; j++) {
                    Rectangle2D testRec = new Rectangle2D.Float(i * LSIZE, j * LSIZE, LSIZE, LSIZE);
                    if(i==currentAreaRow&&j==currentAreaColumn){
                        Graphics2D graphics2D1 = (Graphics2D)g;
                        AlphaComposite ac2 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.2f);
                        graphics2D1.setComposite(ac2);
                        graphics2D1.setPaint(Color.GRAY);
                        graphics2D1.fill(testRec);
                        graphics2D1.setPaint(Color.black);
                        graphics2D1.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
                    }
                    graphics2D.draw(testRec);
                }
            }
        }
        repaint(0,0,1000,1000);
        for(int i=0;i<allGizmos.size();i++){
            Gizmos test = allGizmos.get(i);
            test.paint(g);
        }
        ball.paint(g);
    }

    /**
     * This method is called when the Timer goes off and we
     * need to move and repaint the ball.
     * @modifies both the ball and the window that this listener owns
     * @effects causes the ball to move and the window to be updated
     * to show the new position of the ball.
     */
    private void update() {
        Rectangle oldPos = ball.boundingBox();


        Rectangle repaintArea;
        if(mode == Anim_mode.RUNNING_MODE) {
            judgeCollispe();
            ball.move(); // make changes to the logical animation state
            repaintArea = oldPos.union(ball.boundingBox());
        }
        else{
            repaintArea = new Rectangle(0,0,(int)(ROWS*LSIZE),(int)(COLUMNS*LSIZE));
        }

        // Have Swing tell the AnimationWindow to run its paint()
        // method.  One could also call repaint(), but this would
        // repaint the entire window as opposed to only the portion that
        // has changed.
        repaint(repaintArea.x, repaintArea.y, repaintArea.width,
                repaintArea.height);

        for(int i=0;i<allGizmos.size();i++){
            Gizmos test = allGizmos.get(i);
            for(Gizmos gizmos: test.getConnectedGizmos()){
                gizmos.action();
            }
        }
    }

    /**
     * 设置动画窗口模式
     * @param m
     */
    public void setMode(Anim_mode m) {

        if (this.mode == m) {
            // Nothing to do.
            return;
        }

        if (mode == Anim_mode.EDIT_MODE) {
            // we're about to change mode: turn off all the old listeners
            repaint(0,0,1000,1000);
            removeMouseListener(eventListener);
            removeMouseMotionListener(eventListener);
            removeKeyListener(eventListener);
        }
        if(mode == Anim_mode.RUNNING_MODE){
            removeMouseListener(eventListener);
            removeMouseMotionListener(eventListener);
            removeKeyListener(eventListener);
        }

        mode = m;

        if (mode == Anim_mode.RUNNING_MODE) {
            // the mode is true: turn on the listeners
            addMouseListener(eventListener);
            addMouseMotionListener(eventListener);
            addKeyListener(eventListener);
            requestFocus(); // make sure keyboard is directed to us
            timer.start();
        }
        else if(mode == Anim_mode.EDIT_MODE){
            // the mode is true: turn on the listeners
            addMouseListener(eventListener);
            addMouseMotionListener(eventListener);
            addKeyListener(eventListener);
            requestFocus(); // make sure keyboard is directed to us
            timer.start();
        }
        else {
            timer.stop();
        }
    }

    public Anim_mode getMode(){
        return mode;
    }

    /**
     * 设定当前选中小物件的类型
     * @param gizmos_type
     * @param isNewItem
     */
    public void setChoosedType(Gizmos.Gizmos_type gizmos_type,boolean isNewItem){
        this.currentType = gizmos_type;
        this.isNewItem = isNewItem;
    }

    /**
     * 取消添加新物件
     */
    public void quitAddNewItem(){
        isNewItem = false;
    }

    /**
     * 在绘图窗口中选择一个已绘小物件
     */
    private void chooseGizmos(){
        if(!isItemChosed || isAddConnectState) {
            Gizmos lastChoosedGizmos = currentChoosedGizmos;
            currentChoosedGizmos = findGizmosMouseChosed();
            if (currentChoosedGizmos == null)
                return;
            if(isAddConnectState == false) {
                currentChoosedGizmos.startFocusAnimation();
                isItemChosed = true;
                System.out.println("chosedGizmos id is " + currentChoosedGizmos.getGizmosID());
            }
            else{
                lastChoosedGizmos.addConnectedGizmos(currentChoosedGizmos);
                System.out.println("conect gizmos:");
                System.out.println("lastChoosedGizmos id is " + lastChoosedGizmos.getGizmosID());
                System.out.println("chosedGizmos id is " + currentChoosedGizmos.getGizmosID());
            }
        }
    }

    /**
     * 取消物件的选中
     */
    public void quitChoose(){
        if(isItemChosed){
            isItemChosed = false;
            if(currentChoosedGizmos != null){
                currentChoosedGizmos.startLeaveAnimation();
                currentChoosedGizmos = null;
            }
        }
    }

    /**
     * 得到当前选中的小物件
     * @return
     */
    public Gizmos getCurrentChoosedGizmos(){
        return  currentChoosedGizmos;
    }

    /**
     * 设定是否请求连接的状态
     * @param queryOrNot 是否处于连接状态
     */
    public void queryConnectedGizmos(boolean queryOrNot){
        isAddConnectState = queryOrNot;
    }

    /**
     * 得到当前鼠标位置选中的小物件
     * @return
     */
    public Gizmos findGizmosMouseChosed(){
        for(int i = 0; i < allGizmos.size(); i++){
            Gizmos gizmos = allGizmos.get(i);
            if(gizmos.inGizmos(mousePosition)){
                return  gizmos;
            }
        }
        return null;
    }

    /**
     * 删除小物件
     * @param gizmosId 小物件的id
     */
    public void deleteGizmosById(String gizmosId){
        for(Gizmos gizmos: allGizmos){
            if(gizmos.getGizmosID().equals(gizmosId)){
                allGizmos.remove(gizmos);
                break;
            }
        }
    }

    private void addNewGizmos(){
        switch (currentType){
            case SQURE:
                addNewSqure();
                break;
            case TRANGLE:
                addNewTriangular();
                break;
            case CIRCLE:
                addNewCircle();
                break;
            case FLIPPER_LEFT:
                addNewFlipper(Flipper.Flipper_type.LEFT);
                break;
            case FLIPPER_RIGHT:
                addNewFlipper(Flipper.Flipper_type.RIGHT);
            case LBLOCK:
                addNewLBlock();
                break;
        }
    }

    private void addNewSqure(){
        Squre newSqure = new Squre(this,currentAreaRow*LSIZE+LSIZE/2,currentAreaColumn*LSIZE+LSIZE/2,LSIZE,LSIZE);
        newSqure.setType(Gizmos.Gizmos_type.SQURE);
        newSqure.setGizmosID(getNextId(Gizmos.Gizmos_type.SQURE));
        allGizmos.add(newSqure);
    }

    private void addNewLBlock(){
        LBlock newSqure = new LBlock(this,currentAreaRow*LSIZE+LSIZE/2,currentAreaColumn*LSIZE+LSIZE/2,LSIZE);
        newSqure.setType(Gizmos.Gizmos_type.LBLOCK);
        newSqure.setGizmosID(getNextId(Gizmos.Gizmos_type.LBLOCK));
        allGizmos.add(newSqure);
    }

    private void addNewTriangular(){
        Triangular newTriangular = new Triangular(this,currentAreaRow*LSIZE+LSIZE/2,currentAreaColumn*LSIZE+LSIZE/2,LSIZE,LSIZE/2,LSIZE);
        newTriangular.setType(Gizmos.Gizmos_type.TRANGLE);
        newTriangular.setGizmosID(getNextId(Gizmos.Gizmos_type.TRANGLE));
        allGizmos.add(newTriangular);
    }

    private void addNewCircle(){
        Circle newCircle = new Circle(this,currentAreaRow*LSIZE+LSIZE/2,currentAreaColumn*LSIZE+LSIZE/2,LSIZE/2);
        newCircle.setType(Gizmos.Gizmos_type.CIRCLE);
        newCircle.setGizmosID(getNextId(Gizmos.Gizmos_type.CIRCLE));
        allGizmos.add(newCircle);
    }

    private void addNewFlipper(Flipper.Flipper_type type){
        if(type == Flipper.Flipper_type.LEFT){
            Flipper testLeftFlipper = new Flipper(this,currentAreaRow*LSIZE+LSIZE-FLIPPER_HEIGHT/2,currentAreaColumn*LSIZE+FLIPPER_HEIGHT/2,LSIZE*2,FLIPPER_HEIGHT, type);
            testLeftFlipper.setType(Gizmos.Gizmos_type.FLIPPER_LEFT);
            testLeftFlipper.setGizmosID(getNextId(Gizmos.Gizmos_type.FLIPPER_LEFT));
            allGizmos.add(testLeftFlipper);
        }
        if(type == Flipper.Flipper_type.RIGHT){
            Flipper testRightFlipper = new Flipper(this,currentAreaRow*LSIZE+FLIPPER_HEIGHT/2,currentAreaColumn*LSIZE+FLIPPER_HEIGHT/2,LSIZE*2,FLIPPER_HEIGHT, type);
            testRightFlipper.setType(Gizmos.Gizmos_type.FLIPPER_RIGHT);
            testRightFlipper.setGizmosID(getNextId(Gizmos.Gizmos_type.FLIPPER_RIGHT));
            allGizmos.add(testRightFlipper);
        }
    }

    /**
     * 得到此类型中最后一个小物件的id
     * @precondition: 同类小物件的id都按照数字顺序排序
     * @param type
     * @return
     */
    private String getNextId(Gizmos.Gizmos_type type){
        Gizmos lastGizmosInThisType = null;
        for(int i = allGizmos.size() - 1; i >= 0 ;i--){
            if(allGizmos.get(i).getType() == type){
                lastGizmosInThisType = allGizmos.get(i);
                break;
            }
        }
        int num = 0;
        if(lastGizmosInThisType != null){
            if(type == Gizmos.Gizmos_type.FLIPPER_RIGHT || type == Gizmos.Gizmos_type.FLIPPER_LEFT || type == Gizmos.Gizmos_type.LBLOCK)
                num = Integer.parseInt(lastGizmosInThisType.getGizmosID().substring(2)) + 1;
            else
                num = Integer.parseInt(lastGizmosInThisType.getGizmosID().substring(1)) + 1;
        }
        switch (type){
            case SQURE:
                return SQURE_LETTER+num;
            case TRANGLE:
                return TRANGLE_LETTER+num;
            case CIRCLE:
                return CIRCLE_LETTER+num;
            case FLIPPER_LEFT:
                return FLIPPER_LEFT_LETTER+num;
            case FLIPPER_RIGHT:
                return FLIPPER_RIGHT_LETTER+num;
            case LBLOCK:
                return LBLOCK_LETTER+num;
            default:
                break;
        }
        return "";
    }



    private void judgeCollispe(){
        for(int i=0;i<allGizmos.size();i++){
            allGizmos.get(i).judgeCollispeWithBall(ball);
        }
    }


    /**
     * 得到所有gizmos
     * @return
     */
    public ArrayList<Gizmos> getAllGizmos(){
        return allGizmos;
    }

    /**
     * 得到小球
     * @return
     */
    public BouncingBall getBall(){
        return ball;
    }


    /**
     * Overview: AnimationEventListener is an inner class that
     * responds to all sorts of external events, and provides the
     * required semantic operations for our particular program.  It
     * owns, and sends semantic actions to the ball and window of the
     * outer class
     */
    class AnimationEventListener extends MouseAdapter implements
            MouseMotionListener, KeyListener, ActionListener {

        // MouseAdapter gives us empty methods for the MouseListener
        // interface: mouseClicked, mouseEntered, mouseExited, mousePressed,
        // and mouseReleased.

        /**
         * For this example we only need to override mouseClicked
         * @modifes the ball that this listener owns
         * @effects causes the ball to be bumped in a random direction
         * @param e Detected MouseEvent
         */
        @Override public void mouseClicked(MouseEvent e) {
            ball.randomBump();
            if(isNewItem)
                addNewGizmos();
            else
                chooseGizmos();
        }

        /**
         * MouseMotionListener interface
         * Override this method to act on mouse drag events.
         * @param e Detected MouseEvent
         */
        public void mouseDragged(MouseEvent e) {
        }

        /**
         * MouseMotionListener interface
         * Override this method to act on mouse move events.
         * @param e Detected MouseEvent
         */
        public void mouseMoved(MouseEvent e) {
            int x=e.getX();
            int y=e.getY();
            mousePosition = new Point(x,y);
            currentAreaRow = (int)(x/LSIZE);
            currentAreaColumn = (int)(y/LSIZE);
        }

        /**
         * We implement the KeyListener interface so that we can bump the ball in a
         * random direction if keys A-J is presse.
         * @modifies the ball that this listener owns
         * @effects causes the ball to be bumped in a random direction but
         * only if one of the keys A-J is pressed.
         * @param e Detected Key Press Event
         */
        public void keyPressed(KeyEvent e) {
            //
            int keynum = e.getKeyCode();
            System.out.println("keypress " + e.getKeyCode());
            if ((keynum >= 65) && (keynum <= 74)) {
                System.out.println("keypress " + e.getKeyCode());
                ball.addForce(5,5);
                ball.randomBump();
            }
            if (keynum == 37) {
                System.out.println("keypress " + e.getKeyCode());
                ball.addForce(-5,0);
            }
            if (keynum == 38) {
                System.out.println("keypress " + e.getKeyCode());
                ball.addForce(0,-5);
            }
            if (keynum == 39) {
                System.out.println("keypress " + e.getKeyCode());
                ball.addForce(5,0);
            }
            if (keynum == 40) {
                System.out.println("keypress " + e.getKeyCode());
                ball.addForce(0,-5);
            }
            //"Z"键
            if (keynum == 90) {
                System.out.println("keypress " + e.getKeyCode());
                //testLeftFlipper.action();
            }
            //"/"键
            if (keynum == 47) {
                System.out.println("keypress " + e.getKeyCode());
                //testRightFlipper.action();
            }
        }

        /**
         * Do nothing.
         * @param e Detected Key Released Event
         */
        public void keyReleased(KeyEvent e) {
        }

        /**
         * Do nothing.
         * @param e Detected Key Typed Event
         */
        public void keyTyped(KeyEvent e) {
        }

        /**
         * This is the callback for the timer
         * @param e ActionEvent generated by timer
         */
        public void actionPerformed(ActionEvent e) {
            update();
        }

    }
}

